
#include <cpu.h>
#include "exception.h"
#include "pentium3.h"

/* ------------------------------------------------------------------------- */

static bool fault;

/* ------------------------------------------------------------------------- */

static inline bool check_double_fault(void);

static inline void raise_divide_error(void);					/* #DE */
static inline void raise_debug(void);							/* #DB */
static inline void raise_break_point(void);						/* #BP */
static inline void raise_overflow(void);						/* #OF */
static inline void raise_bound_range(void);						/* #BR */
static inline void raise_undefined_opcode(void);				/* #UD */
static inline void raise_no_math_coprocessor(void);				/* #NM */
static inline void raise_invalid_tss(void);						/* #TS */
static inline void raise_segment_not_present(void);				/* #NP */
static inline void raise_stack_segment_fault(void);				/* #SS */
static inline void raise_general_protection(uint32 error);		/* #GP */
static inline void raise_page_fault(void);						/* #PF */
static inline void raise_math_fault(void);						/* #MF */
static inline void raise_alignment_check(void);					/* #AC */
static inline void raise_machine_check(void);					/* #MC */
static inline void raise_streaming_simd_extensions(void);		/* #XF */

/* ------------------------------------------------------------------------- */

void fastcall reset_exception(void)
{
	fault = false;
}

void fastcall raise_exception_with_error(uint8 vector_no, uint32 error_code)
{
	/* double fault? */
	if (check_double_fault())
		return;

	switch (vector_no) {
	case EX_DE:
		raise_divide_error();
		break;
	case EX_DB:
		raise_debug();
		break;
	case EX_BP:
		raise_break_point();
		//break;
		return;
	case EX_OF:
		raise_overflow();
		break;
	case EX_BR:
		raise_bound_range();
		break;
	case EX_UD:
		raise_undefined_opcode();
		break;
	case EX_NM:
		raise_no_math_coprocessor();
		break;
	case EX_TS:
		raise_invalid_tss();
		break;
	case EX_NP:
		raise_segment_not_present();
		break;
	case EX_SS:
		raise_stack_segment_fault();
		break;
	case EX_GP:
		raise_general_protection(error_code);
		break;
	case EX_PF:
		raise_page_fault();
		break;
	case EX_MF:
		raise_math_fault();
		break;
	case EX_AC:
		raise_alignment_check();
		break;
	case EX_MC:
		raise_machine_check();
		break;
	case EX_XF:
		raise_streaming_simd_extensions();
		break;
	default:
		assert(0);
		return;
	}

	resume_piii();
}

/* ------------------------------------------------------------------------- */

static inline void raise_divide_error(void)
{
	write_log("[CPU] exception : divide error");
}

static inline void raise_debug(void)
{
	write_log("[CPU] exception : debug");
}

static inline void raise_break_point(void)
{
	uint32 fastcall virtual_read(uint16 sel, uint64 desc, uint32 va, size_t size);

	write_log("[CPU] exception : break point");
	write_log("\tEAX = %08Xh\tECX = %08Xh\tEDX = %08Xh\tEBX = %08Xh", eax, ecx, edx, ebx);
	write_log("\tESP = %08Xh\tEBP = %08Xh\tESI = %08Xh\tEDI = %08Xh", esp, ebp, esi, edi);
	write_log("\tCS = %04Xh\tDS = %04Xh\tES = %04Xh\tFS = %04Xh\tGS = %04Xh", cs, ds, es, fs, gs);
	write_log("\treturn address = %08Xh", virtual_read(ss, xss, ebp + 4, 4));
	write_log("\tparam1 = %08Xh", virtual_read(ss, xss, ebp + 8, 4));
	write_log("\tparam2 = %08Xh", virtual_read(ss, xss, ebp + 12, 4));
	write_log("\tparam3 = %08Xh", virtual_read(ss, xss, ebp + 16, 4));
	write_log("\tparam4 = %08Xh", virtual_read(ss, xss, ebp + 20, 4));
	write_log("\torg esp = %08Xh", virtual_read(ss, xss, ebp, 4));
	write_log("\tlocal1 = %08Xh", virtual_read(ss, xss, ebp - 4, 4));
	write_log("\tlocal2 = %08Xh", virtual_read(ss, xss, ebp - 8, 4));
	write_log("\tlocal3 = %08Xh", virtual_read(ss, xss, ebp - 12, 4));
	write_log("\tlocal4 = %08Xh", virtual_read(ss, xss, ebp - 16, 4));

	__asm int 3;
}

static inline void raise_overflow(void)
{
	write_log("[CPU] exception : overflow");
}

static inline void raise_bound_range(void)
{
	write_log("[CPU] exception : bound range");
}

static inline void raise_undefined_opcode(void)
{
	write_log("[CPU] exception : undefined opcode");
}

static inline void raise_no_math_coprocessor(void)
{
	write_log("[CPU] exception : no math coprocessor");
}

static inline void raise_invalid_tss(void)
{
	write_log("[CPU] exception : invalid tss");
}

static inline void raise_segment_not_present(void)
{
	write_log("[CPU] exception : segment not present");
}

static inline void raise_stack_segment_fault(void)
{
	write_log("[CPU] exception : stack segment fault");
}

static inline void raise_general_protection(uint32 error)
{
	write_log("[CPU] exception : general protection %d", error);
}

static inline void raise_page_fault(void)
{
	write_log("[CPU] exception : page fault");
}

static inline void raise_math_fault(void)
{
	write_log("[CPU] exception : math fault");
}

static inline void raise_alignment_check(void)
{
	write_log("[CPU] exception : alignment check");
}

static inline void raise_machine_check(void)
{
	write_log("[CPU] exception : machine check");
}

static inline void raise_streaming_simd_extensions(void)
{
	write_log("[CPU] exception : streaming simd extensions");
}

static inline bool check_double_fault(void)
{
	if (fault) {
#ifndef NDEBUG
		write_log("[CPU] exception : double fault, perform reset.");
#endif
		reset_piii();
		resume_piii();
	}

	fault = true;
	return false;
}

/* ------------------------------------------------------------------------- */
